package fi.pyppe.android.gps;

import java.io.File;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Paint.Style;
import android.location.Location;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;

import fi.pyppe.android.gps.domain.TaggedPlacemark;

/**
 * MapsActivity for showing the KML within Google Maps.
 * 
 * @author Pyppe
 */
public class MapsActivity extends MapActivity {
  
  private MapView mapView; 
  private MapController mc;
  private List<GeoPoint> lineOfPlacemarks;
  private List<TaggedPlacemark> taggedPlacemarks;
  private String startTime;
  private String endTime;
  private Long duration;
  private Integer distance;
  
  
  class MapPathOverlay extends Overlay {
    @Override
    public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when) {
      super.draw(canvas, mapView, shadow);

      if (lineOfPlacemarks != null && lineOfPlacemarks.size() > 0) {
        Paint paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.RED);
        paint.setStrokeWidth(1.5F);
        paint.setAntiAlias(false);
        Path path = new Path();
        for (int i=0; i < lineOfPlacemarks.size(); i++) {
          Point screenPoint = new Point();
          GeoPoint geoPoint = lineOfPlacemarks.get(i);
          mapView.getProjection().toPixels(geoPoint, screenPoint);
          if (i == 0) {
            path.moveTo(screenPoint.x, screenPoint.y);
          } else {
            path.lineTo(screenPoint.x, screenPoint.y);
          }
        }
        canvas.drawPath(path, paint);
      }
      
      if (taggedPlacemarks != null && taggedPlacemarks.size() > 0) {
        for (TaggedPlacemark placemark : taggedPlacemarks) {
          Point screenPoint = new Point();
          mapView.getProjection().toPixels(placemark.getGeoPoint(), screenPoint);
          
          Paint paint = new Paint();
          paint.setAntiAlias(true);
          
          // Draw text
          paint.setStyle(Style.FILL);
          paint.setARGB(200, 0, 0, 0);
          canvas.drawText(placemark.getDescription(), screenPoint.x + 7, screenPoint.y, paint);

          // Change the paint to a Green 
          paint.setARGB(80, 156, 192, 36);
          paint.setStrokeWidth(1);
          // Draw an oval around our location placemark
          RectF oval = new RectF(screenPoint.x - 5, screenPoint.y + 5,  screenPoint.x + 5, screenPoint.y - 5);
          canvas.drawOval(oval, paint);

          // Draw a black stroke around the oval we drew before
          paint.setARGB(100,100,100,100);
          paint.setStyle(Style.STROKE);
          canvas.drawCircle(screenPoint.x, screenPoint.y, 5, paint);
        }
      }
      return true;
    }
  }
  
  
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.map_view);

    mapView = (MapView) findViewById(R.id.mapView);
    mapView.setBuiltInZoomControls(true);
    mc = mapView.getController();
    
    Bundle extras = getIntent().getExtras();
    if (extras != null) {
      File kmlFile = (File) extras.get("kmlFile");
      try {
        parseKml(kmlFile);
      } catch (Exception e) {
        e.printStackTrace();
      }
      
    }
    if (lineOfPlacemarks != null && lineOfPlacemarks.size() > 0) {
      mc.animateTo(lineOfPlacemarks.get(0));
    }
    mc.setZoom(12);
    
    // Add a location marker
    MapPathOverlay mapOverlay = new MapPathOverlay();
    List<Overlay> listOfOverlays = mapView.getOverlays();
    listOfOverlays.clear();
    listOfOverlays.add(mapOverlay);

    mapView.invalidate();
  }

  @Override
  protected boolean isRouteDisplayed() {
    return false;
  }
  
  private void parseKml(File kmlFile) throws Exception {
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = factory.newDocumentBuilder();
    Document dom = builder.parse(kmlFile);
    Element root = dom.getDocumentElement();
    NodeList lineStrings = root.getElementsByTagName("LineString");
    
    lineOfPlacemarks = new ArrayList<GeoPoint>();
    for (int i = 0; i < lineStrings.getLength(); i++) {
      Node lineString = lineStrings.item(i);
      Node coordinatesNode = lineString.getLastChild();
      String coordinatesString = coordinatesNode.getFirstChild().getNodeValue();
      String[] coordinatePairs =  coordinatesString.split(" ");
      Location previousLocation = null;
      float distanceInMeters = 0;
      for (String coordinatePair : coordinatePairs) {
        Double longitude = Double.parseDouble(coordinatePair.split(",")[0]);
        Double latitude = Double.parseDouble(coordinatePair.split(",")[1]);
        Location location = new Location("");
        location.setLongitude(longitude);
        location.setLatitude(latitude);
        if (previousLocation != null) {
          distanceInMeters += location.distanceTo(previousLocation);
        }
        previousLocation = location;
        GeoPoint p = new GeoPoint((int) (latitude * 1E6), (int) (longitude * 1E6));
        lineOfPlacemarks.add(p);
      }
      if (distanceInMeters > 0) {
        distance = Math.round(distanceInMeters);
      }
    }
    
    NodeList points = root.getElementsByTagName("Point");
    taggedPlacemarks = new ArrayList<TaggedPlacemark>();
    for (int i = 0; i < points.getLength(); i++) {
      Node pointNode = points.item(i);
      String[] coordinatePairs =  pointNode.getFirstChild().getFirstChild().getNodeValue().split(" ");
      Double longitude = Double.parseDouble(coordinatePairs[0].split(",")[0]);
      Double latitude = Double.parseDouble(coordinatePairs[0].split(",")[1]);
      String description = pointNode.getParentNode().getFirstChild().getFirstChild().getNodeValue();
      GeoPoint p = new GeoPoint((int) (latitude * 1E6), (int) (longitude * 1E6));
      TaggedPlacemark taggedPlacemark = new TaggedPlacemark(p, description);
      taggedPlacemarks.add(taggedPlacemark);
    }
    
    NodeList timeSpan = root.getElementsByTagName("TimeSpan");
    if (timeSpan.getLength() == 1) {
      startTime = timeSpan.item(0).getFirstChild().getFirstChild().getNodeValue();
      endTime = timeSpan.item(0).getLastChild().getFirstChild().getNodeValue();
    }
    NodeList data = root.getElementsByTagName("Data");
    if (data.getLength() == 1) {
      Node dataNode = data.item(0);
      NamedNodeMap attributes = dataNode.getAttributes();
      Node nameAttribute = attributes.getNamedItem("name");
      if (nameAttribute.getNodeValue().equals(PyppeGPSConstants.KML_EXTENDED_DATA_DURATION)) {
        duration = Long.parseLong(dataNode.getFirstChild().getFirstChild().getNodeValue());
      }
    }
    
  }
  
  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.maps_menu, menu);
    return true;
  }

  /**
   * Called when one of the menu items is selected.
   */
  public boolean onOptionsItemSelected(MenuItem item) {
    int itemId = item.getItemId();

    switch (itemId) {
    case R.id.mapsMenuDetails:
      showDetailsDialog();
      break;
    }
    return false;
  }
  
  private String convertMetersToDistanceString(int meters) {
    if (meters < 1000) {
      return meters + " m";
    } else {
      NumberFormat nf = new DecimalFormat("######.0");
      double km = (double) meters / 1000;
      return nf.format(km) + " km";
    }
  }
  
  private void showDetailsDialog() {
    StringBuilder sb = new StringBuilder();
    if (startTime != null) {
      sb.append("Start: " + startTime + System.getProperty("line.separator"));
    }
    if (endTime != null) {
      sb.append("End: " + endTime + System.getProperty("line.separator"));
    }
    if (duration != null) {
      sb.append("Duration: " + convertSecondsToDurationString(duration) + System.getProperty("line.separator"));
    }
    if (distance != null) {
      sb.append("Distance: " + convertMetersToDistanceString(distance) + System.getProperty("line.separator"));
    }
    if (distance != null && duration != null) {
      NumberFormat nf = new DecimalFormat("##0.0");
      double kmPerHours = (double) (distance/duration * 3.6);
      sb.append("Average speed: " + nf.format(kmPerHours) + "km/h" + System.getProperty("line.separator"));
    }
    
    AlertDialog.Builder builder;
    AlertDialog alertDialog;

    Context mContext = this;
    LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE);
    View layout = inflater.inflate(R.layout.details, (ViewGroup) findViewById(R.id.detailsLayout));

    TextView text = (TextView) layout.findViewById(R.id.detailsText);
    text.setText(sb.toString());

    builder = new AlertDialog.Builder(mContext);
    builder.setView(layout);
    alertDialog = builder.create();
    alertDialog.setTitle("Details");
    alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int which) {
        return;
      }
    });
    alertDialog.show();
  }
  
  private String convertSecondsToDurationString(long seconds) {
    long h = seconds / 3600;
    long remainder = seconds % 3600;
    long min = remainder / 60;
    long sec = remainder % 60; 
    NumberFormat nf = new DecimalFormat("00");
    return nf.format(h) + ":" + nf.format(min) + ":" + nf.format(sec);
  }

}
